package com.mylater.nace.common;

/**
 * The ClassModifier represents 修改Class文件，暂时只提供修改常量池常量的功能
 *
 * @author fengjiachun
 * @version $Id$
 */
public class ClassModifier {

    /**
     * Class文件中常量池的起始偏移
     */
    private static final int CONSTANT_POOL_COUNT_INDEX = 8;

    /**
     * CONSTANT_Utf8_info常量的tag标志
     */
    private static final int CONSTANT_Utf8_info = 1;

    /**
     * 常量池中11种常量所占的长度，CONSTANT_Utf8_info型常量除外，因为它不是定长的
     */
    private static final int[] CONSTANT_ITEM_LENGTH = {
            -1,       // 占位用的
            -1,       // tag 1:CONSTANT_Utf8_info
            -1,       // 占位用的
            4,        // tag 3:CONSTANT_Integer_info
            4,        // tag 4:CONSTANT_Float_info
            8,        // tag 5:CONSTANT_Long_info
            8,        // tag 6:CONSTANT_Double_info
            2,        // tag 7:CONSTANT_Class_info
            2,        // tag 8:CONSTANT_String_info
            4,        // tag 9:CONSTANT_Fieldref_info
            4,        // tag 10:CONSTANT_Methodref_info
            4,        // tag 11:CONSTANT_InterfaceMethodref_info
            4         // tag 12:CONSTANT_NameAndType_info
    };

    private static final int u1 = 1;
    private static final int u2 = 2;

    private byte[] classByte;

    public ClassModifier(byte[] classByte) {
        this.classByte = classByte;
    }

    /**
     * 修改常量池中CONSTANT_Utf8_info常量的内容
     *
     * @param oldStr 修改前的字符串
     * @param newStr 修改后的字符串
     * @return 修改结果
     */
    public byte[] modifyUTF8Constant(String oldStr, String newStr) {
        // 获取常量池中常量的数量
        int constantPoolCount = getConstantPoolCount();
        // offset:计算cp_info的开始位置
        int offset = CONSTANT_POOL_COUNT_INDEX + u2;
        for (int i = 1; i < constantPoolCount; i++) {
            // tag为常量池的项目类型
            /**
             * 以下摘自《java虚拟机规范》
             * CONSTANT_Utf8_info tag:1
             * CONSTANT_Integer_info tag:3
             * CONSTANT_Float_info tag:4
             * CONSTANT_Long_info tag:5
             * CONSTANT_Double_info tag:6
             * CONSTANT_Class_info tag:7
             * CONSTANT_String_info tag:8
             * CONSTANT_Fieldref_info tag:9
             * CONSTANT_Methodref_info tag:10
             * CONSTANT_InterfaceMethodref_info tag:11
             * CONSTANT_NameAndType_info tag:12
             */
            int tag = ByteUtils.bytes2Int(this.classByte, offset, u1);
            offset += u1;
            if (tag == CONSTANT_Utf8_info) {
                /**
                 * CONSTANT_Utf8_info数据类型的额结构
                 * 项目 类型 描述
                 * tag u1 值为1
                 * length u2 UTF-8编码的字符串占用的字节数
                 * bytes 长度为length的UTF-8编码格式的字符串
                 */
                // 接下来的两个字节是当前CONSTANT_Utf8_info的数据的长度
                int length = ByteUtils.bytes2Int(this.classByte, offset, u2);
                offset += u2;
                String str = ByteUtils.bytes2String(this.classByte, offset, length);
                if (str.equalsIgnoreCase(oldStr)) {
                    // 把新字符串的内容转为16进制的字节数组
                    byte[] strBytes = ByteUtils.string2Bytes(newStr);
                    // 把新字符串的长度转为16进制的字节数组
                    byte[] strLen = ByteUtils.int2Bytes(newStr.length(), u2);
                    // 替换新字符串的“长度”
                    this.classByte = ByteUtils.bytesReplace(this.classByte, offset - u2, u2, strLen);
                    // 替换新字符串的“内容”
                    this.classByte = ByteUtils.bytesReplace(this.classByte, offset, length, strBytes);
                    return this.classByte;
                } else {
                    offset += length;
                }
            } else {
                offset += CONSTANT_ITEM_LENGTH[tag];
            }
        }
        return this.classByte;
    }

    /**
     * 获取常量池中常量的数量
     *
     * @return 常量池数量
     */
    public int getConstantPoolCount() {
        return ByteUtils.bytes2Int(this.classByte, CONSTANT_POOL_COUNT_INDEX, u2);
    }
}
